/******************************************************************************* * Copyright (c) 2006, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Gorkem Ercan (Red Hat) - Fix for Bug 427142 *******************************************************************************/ package org.eclipse.ui.statushandlers; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.ILogListener; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.application.WorkbenchAdvisor; import org.eclipse.ui.internal.WorkbenchErrorHandlerProxy; import org.eclipse.ui.internal.WorkbenchPlugin; import org.eclipse.ui.internal.misc.StatusUtil; import org.eclipse.ui.internal.statushandlers.StatusHandlerDescriptor; import org.eclipse.ui.internal.statushandlers.StatusHandlerRegistry; import org.eclipse.ui.progress.IProgressConstants; import org.osgi.framework.BundleContext; /** * <p> * StatusManager is the entry point for all statuses to be reported in the user * interface. * </p> * * <p> * Handlers shoudn't be used directly but through the StatusManager singleton * which keeps the status handling policy and chooses handlers. * <code>StatusManager.getManager().handle(IStatus)</code> and * <code>handle(IStatus status, int style)</code> are the methods are the * primary access points to the StatusManager. * </p> * * <p> * Acceptable styles (can be combined with logical OR) * <ul> * <li>NONE - a style indicating that the status should not be acted on. This * is used by objects such as log listeners that do not want to report a status * twice</li> * <li>LOG - a style indicating that the status should be logged only</li> * <li>SHOW - a style indicating that handlers should show a problem to an user * without blocking the calling method while awaiting user response. This is * generally done using a non modal {@link Dialog}</li> * <li>BLOCK - a style indicating that the handling should block the UI * until the user has responded. This is generally done using a modal * window such as a {@link Dialog}</li> * </ul> * </p> * * <p> * Handlers are intended to be accessed via the status manager. The * StatusManager chooses which handler should be used for a particular error. * There are two ways for adding handlers to the handling flow. First using * extension point <code>org.eclipse.ui.statusHandlers</code>, second by the * workbench advisor and its method * {@link WorkbenchAdvisor#getWorkbenchErrorHandler()}. If a handler is * associated with a product, it is used instead of this defined in advisor. * </p> * * @since 3.3 * @see AbstractStatusHandler */ public class StatusManager { /** * A style indicating that the status should not be acted on. This is used * by objects such as log listeners that do not want to report a status * twice. */ public static final int NONE = 0; /** * A style indicating that the status should be logged only. */ public static final int LOG = 0x01; /** * A style indicating that handlers should show a problem to an user without * blocking the calling method while awaiting user response. This is * generally done using a non modal {@link Dialog}. */ public static final int SHOW = 0x02; /** * A style indicating that the handling should block the calling thread * until the status has been handled. * <p> * A typical usage of this would be to ensure that the user's actions are * blocked until they've dealt with the status in some manner. It is * therefore likely but not required that the <code>StatusHandler</code> * would achieve this through the use of a modal dialog. * </p><p>Due to the fact * that use of <code>BLOCK</code> will block UI, care should be * taken in this use of this flag. * </p> */ public static final int BLOCK = 0x04; private static volatile StatusManager MANAGER; private volatile AbstractStatusHandler statusHandler; private List loggedStatuses = new ArrayList(); private ListenerList<INotificationListener> listeners = new ListenerList<>(); /** * Returns StatusManager singleton instance. * * @return the manager instance */ public static StatusManager getManager() { if (MANAGER != null) { return MANAGER; } synchronized (StatusManager.class) { if (MANAGER == null) { MANAGER = new StatusManager(); } } return MANAGER; } private StatusManager() { Platform.addLogListener(new StatusManagerLogListener()); } private AbstractStatusHandler getStatusHandler(){ if (statusHandler != null) { return statusHandler; } BundleContext bundleContext = WorkbenchPlugin.getDefault().getBundle().getBundleContext(); if (bundleContext == null) { // bundle is not in the STARTING, ACTIVE, or STOPPING state: we // should not do anything, most likely we are going to shut down return null; } StatusHandlerDescriptor defaultHandlerDescriptor = StatusHandlerRegistry.getDefault() .getDefaultHandlerDescriptor(); synchronized (this) { if (statusHandler == null) { if (defaultHandlerDescriptor != null) { try { statusHandler = defaultHandlerDescriptor.getStatusHandler(); } catch (CoreException ex) { logError("Errors during the default handler creating", ex); //$NON-NLS-1$ } } if (statusHandler == null) { statusHandler = new WorkbenchErrorHandlerProxy(); } } } return statusHandler; } /** * Handles the given status adapter due to the style. Because the facility * depends on Workbench, this method will log the status, if Workbench isn't * initialized and the style isn't {@link #NONE}. If Workbench isn't * initialized and the style is {@link #NONE}, the manager will do nothing. * * @param statusAdapter * the status adapter * @param style * the style. Value can be combined with logical OR. One of * {@link #NONE}, {@link #LOG}, {@link #SHOW} and * {@link #BLOCK}. */ public void handle(StatusAdapter statusAdapter, int style) { try { // The manager will only log the error when the status adapter or // the embedded status is null. if (statusAdapter == null) { logError( "Error occurred during status handling",//$NON-NLS-1$ new NullPointerException("StatusAdapter object is null")); //$NON-NLS-1$ return; } if (statusAdapter.getStatus() == null) { logError("Error occurred during status handling",//$NON-NLS-1$ new NullPointerException("Status object is null")); //$NON-NLS-1$ return; } // The manager will only log the status, if Workbench isn't // initialized and the style isn't NONE. If Workbench isn't // initialized and the style is NONE, the manager will do nothing. if (!PlatformUI.isWorkbenchRunning()) { if (style != StatusManager.NONE) { logError(statusAdapter.getStatus()); } return; } // delegates the problem to workbench handler AbstractStatusHandler handler = getStatusHandler(); if (handler != null) { handler.handle(statusAdapter, style); } else if (style != StatusManager.NONE) { logError(statusAdapter.getStatus()); } // if attached status handler is not able to notify StatusManager // about particular event, use the default policy and fake the // notification if (handler == null || !handler.supportsNotification( INotificationTypes.HANDLED)) { generateFakeNotification(statusAdapter, style); } } catch (Throwable ex) { // The used status handler failed if (statusAdapter != null) { logError(statusAdapter.getStatus()); } logError("Error occurred during status handling", ex); //$NON-NLS-1$ } } /** * Handles the given status adapter. The {@link #LOG} style is used when * this method is called. * * @param statusAdapter * the status adapter */ public void handle(StatusAdapter statusAdapter) { handle(statusAdapter, StatusManager.LOG); } /** * Handles the given status due to the style. Because the facility depends * on Workbench, this method will log the status, if Workbench isn't * initialized and the style isn't {@link #NONE}. If Workbench isn't * initialized and the style is {@link #NONE}, the manager will do nothing. * * @param status * the status to handle * @param style * the style. Value can be combined with logical OR. One of * {@link #NONE}, {@link #LOG}, {@link #SHOW} and * {@link #BLOCK}. */ public void handle(IStatus status, int style) { StatusAdapter statusAdapter = new StatusAdapter(status); handle(statusAdapter, style); } /** * Handles the given status. The {@link #LOG} style is used when this method * is called. * * @param status * the status to handle */ public void handle(IStatus status) { handle(status, StatusManager.LOG); } /** * Handles given CoreException. This method has been introduced to prevent * anti-pattern: <br/><code> * StatusManager.getManager().handle(coreException.getStatus()); * </code><br/> * that does not print the stack trace to the log. * * @param coreException * a CoreException to be handled. * @param pluginId * the unique identifier of the relevant plug-in * @see StatusManager#handle(IStatus) * @since 3.4 * */ public void handle(CoreException coreException,String pluginId) { IStatus exceptionStatus = coreException.getStatus(); handle(new Status(exceptionStatus.getSeverity(), pluginId, coreException .getLocalizedMessage(), coreException)); } /** * This method informs the StatusManager that this IStatus is being handled * by the handler and to ignore it when it shows up in our ILogListener. * * @param status * already handled and logged status */ public void addLoggedStatus(IStatus status) { loggedStatuses.add(status); } private void logError(String message, Throwable ex) { IStatus status = StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH, message, ex); addLoggedStatus(status); WorkbenchPlugin.log(status); } private void logError(IStatus status) { addLoggedStatus(status); WorkbenchPlugin.log(status); } /** * This log listener handles statuses added to a plug-in's log. If our own * WorkbenchErrorHandler inserts it into the log, then ignore it. * * @see #addLoggedStatus(IStatus) * @since 3.3 */ private class StatusManagerLogListener implements ILogListener { @Override public void logging(IStatus status, String plugin) { if (!loggedStatuses.contains(status)) { handle(status, StatusManager.NONE); } else { loggedStatuses.remove(status); } } } /** * This method should be called by custom status handlers when an event * occurs. This method has no effect if statushandler does not support * particular event type. * * @param type * - type of the event. * @param adapters * - array of affected {@link StatusAdapter}s. * @see INotificationTypes * @see AbstractStatusHandler#supportsNotification(int) * @since 3.5 */ public void fireNotification(int type, StatusAdapter[] adapters){ AbstractStatusHandler handler = getStatusHandler(); if (handler != null && handler.supportsNotification(type)) { doFireNotification(type, adapters); } } private void doFireNotification(int type, StatusAdapter[] adapters) { for (INotificationListener listener : listeners) { listener.statusManagerNotified(type, adapters); } } private void generateFakeNotification(StatusAdapter statusAdapter, int style) { if (((style & StatusManager.SHOW) == StatusManager.SHOW || (style & StatusManager.BLOCK) == StatusManager.BLOCK) && statusAdapter .getProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY) != Boolean.TRUE) { doFireNotification(INotificationTypes.HANDLED, new StatusAdapter[] { statusAdapter }); } } /** * Adds a listener to the StatusManager. * * @param listener * - a listener to be added. * @since 3.5 */ public void addListener(INotificationListener listener) { this.listeners.add(listener); } /** * Removes a listener from StatusManager. * * @param listener * - a listener to be removed. * @since 3.5 */ public void removeListener(INotificationListener listener){ this.listeners.remove(listener); } /** * This interface allows for listening to status handling framework changes. * Currently it is possible to be notified when: * <ul> * <li>all statuses has been handled</li>. * </ul> * @since 3.5 * */ public interface INotificationListener{ /** * * @param type * - a type of notification. * @param adapters * - affected {@link StatusAdapter}s */ public void statusManagerNotified(int type, StatusAdapter[] adapters); } /** * This interface declares types of notification. * * @since 3.5 * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. * */ public interface INotificationTypes { /** * This type notifications are used when a particular * {@link StatusAdapter} was handled. */ public static final int HANDLED = 0x01; } }